package com.te;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import org.jbox2d.collision.AABB;
import org.jbox2d.collision.CircleShape;
import org.jbox2d.collision.PolygonShape;
import org.jbox2d.collision.Shape;
import org.jbox2d.collision.ShapeType;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.World;

public class GameWorld extends SurfaceView implements SurfaceHolder.Callback {
    private ViewThread          mThread;

    public static int           height;
    public static int           width;
    private static Paint        circlePaint            = new Paint();
    private static Paint        aABBPaint              = new Paint();
    private static Paint        borderPaint            = new Paint();
    private static final String TAG                    = GameWorld.class.getSimpleName();

    private AABB                aABB;
    public World                world;

    private static final float  timeStep               = 1f / 40f;
    private static final int    iterations             = 8;

    private boolean             ballWaitingToBeCreated = false;
    private float               xForBallWaitingToBeCreated, yForBallWaitingToBeCreated;
    Bitmap                      ballImage;
    Bitmap                      groundImage;
    Bitmap tempBitMap;

    private Body                ballWaitingToBeDeleted = null;


    //create no objects in main loop to avoid GC
    private static float left, right, top, bottom, centerX, centerY;
    private static Body currentBody;
    private static Shape currentShape;
    private static Rect destRectForBalls, rectForBorder, rectForAABB;
    private static int radius;
    private static Vec2[] vecList;
    private static Matrix matrix, identityMatrix;



    public GameWorld(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
    }

    public GameWorld(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    public GameWorld(Context context, int screenHeight, int screenWidth) {
        super(context);
        // TODO Auto-generated constructor stub
        getHolder().addCallback(this);

        Log.d(TAG, screenHeight + " : " + screenWidth);

        mThread = new ViewThread(this);

    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.d(TAG, "surface " + height + " X " + width + " changed");
        GameWorld.width = width;
        GameWorld.height = height;
        create2DBoxWorld();
        if (!mThread.isAlive()) {
            mThread = new ViewThread(this);
            mThread.setRunning(true);
            mThread.start();
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(TAG, "surface created");

    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        if (mThread.isAlive()) {
            mThread.setRunning(false);
        }

    }

    public void create2DBoxWorld() {

        destRectForBalls = new Rect(0,0,0,0);
        rectForBorder = new Rect((int) left, (int) top, (int) right, (int) bottom);

        borderPaint.setARGB(255, 0, 105, 205);
        borderPaint.setStyle(Paint.Style.FILL);
        borderPaint.setAntiAlias(true);
        borderPaint.setTextSize(20);
        borderPaint.setTextSkewX(-0.2f);
        borderPaint.setTextScaleX(1.5f);
        circlePaint.setARGB(255, 0, 0, 255);
        aABBPaint.setARGB(255, 255, 255, 0);
        ballImage = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
        groundImage = BitmapFactory.decodeResource(getResources(), R.drawable.grass);
        matrix = new Matrix();
        identityMatrix = new Matrix();

        // Step 1: Create Physics World Boundaries
        aABB = new AABB();
        float padding = 0.1f;
        aABB.lowerBound.set(new Vec2(padding, padding));
        aABB.upperBound.set(new Vec2(BodyUtil.scaleFromAndroidPixelCoord(width) - padding, BodyUtil
                .scaleFromAndroidPixelCoord(height) - padding));

        int aABBLeft = BodyUtil.scaleToAndroidPixelCoord(aABB.lowerBound.x);
        int aABBTop = BodyUtil.scaleToAndroidPixelCoord(aABB.lowerBound.y);
        int aABBRight = BodyUtil.scaleToAndroidPixelCoord(aABB.upperBound.x);
        int aABBBottom = BodyUtil.scaleToAndroidPixelCoord(aABB.upperBound.y);

        rectForAABB = new Rect(aABBLeft, aABBTop, aABBRight, aABBBottom);



        // Step 2: Create Physics World with Gravity
        Vec2 gravity = new Vec2((float) 0.0, (float) 0.0);
        boolean doSleep = true;
        world = new World(aABB, gravity, doSleep);

        // Step 3: Create Borders
        BodyUtil.createBorders(world, 4, 0.1f, padding, padding, BodyUtil.scaleFromAndroidPixelCoord(width) - padding,
                BodyUtil.scaleFromAndroidPixelCoord(height) - padding);
    }

    public void doDraw(String fPS, Canvas canvas) {

        canvas.drawBitmap(groundImage, null, rectForAABB, aABBPaint);

        currentBody = world.getBodyList();
        // Log.d(TAG, "number of bodies" + world.getBodyCount());
        for(int k = 0; k < world.getBodyCount(); k++) {
            // Log.d(TAG, "currentBody number " + k);
            //BodyUserData bodyUserData = (BodyUserData) currentBody.getUserData();
            //if (bodyUserData != null) {
                // Log.d(TAG, "Color " + bodyUserData.color);
            //}
            currentShape = currentBody.getShapeList();
            if (currentShape != null) {

                if (ShapeType.POLYGON_SHAPE.equals(currentShape.getType())) {

                    vecList = ((PolygonShape) currentShape).getVertices();

                    left = Float.MAX_VALUE;
                    top = Float.MAX_VALUE;
                    right = Float.MIN_VALUE;
                    bottom = Float.MIN_VALUE;

                    for(Vec2 vector : vecList) {
                        // Log.d(TAG, "xy " + vector.x + " : " + vector.y);
                        left = Math.min(left, vector.x);
                        top = Math.min(top, vector.y);
                        right = Math.max(right, vector.x);
                        bottom = Math.max(bottom, vector.y);
                    }

                    left = BodyUtil.scaleToAndroidPixelCoord(left + currentBody.getPosition().x);
                    right = BodyUtil.scaleToAndroidPixelCoord(right + currentBody.getPosition().x);
                    top = BodyUtil.scaleToAndroidPixelCoord(top + currentBody.getPosition().y);
                    bottom = BodyUtil.scaleToAndroidPixelCoord(bottom + currentBody.getPosition().y);
                    // Log.d(TAG, "left " + left + " right " + right);
                    // Log.d(TAG, "top " + top + " bottom " + bottom);

                    rectForBorder.set((int) left, (int) top, (int) right, (int) bottom);
                    canvas.drawRect(rectForBorder, borderPaint);

                }
                else if (ShapeType.CIRCLE_SHAPE.equals(currentShape.getType())) {
                    // Log.d(TAG, "drawing circle");
                    radius = BodyUtil.scaleToAndroidPixelCoord(((CircleShape) currentShape).getRadius());
                    centerX = BodyUtil.scaleToAndroidPixelCoord(currentBody.getPosition().x);
                    centerY = BodyUtil.scaleToAndroidPixelCoord(currentBody.getPosition().y);

                    destRectForBalls.set((int) centerX - radius, (int) centerY - radius, (int) centerX + radius,
                            (int) centerY + radius);

                    matrix.set(identityMatrix);
                    //Log.d(TAG,"angularv "+destRectForBalls.height()+":"+destRectForBalls.width());
                    currentShape.setUserData((Float) currentShape.getUserData()+currentBody.getAngularVelocity()*3.14f);
                    matrix.setRotate((Float) currentShape.getUserData());
                    //matrix.postTranslate(centerX-ballImage.getWidth()/2,centerY-ballImage.getHeight()/2);

                    //Bitmap oldBitmap = (Bitmap) currentShape.getUserData();
                    tempBitMap = Bitmap.createBitmap(ballImage, 0, 0,
                          ballImage.getWidth(), ballImage.getHeight(), matrix, false);
                    //Bitmap tempBitMap2 = Bitmap.createScaledBitmap(tempBitMap,64,64,false);
                    //Log.d(TAG,"tempbtimap2 "+tempBitMap.getHeight()+":"+tempBitMap2.getWidth());

                    canvas.drawBitmap(tempBitMap, centerX-tempBitMap.getHeight()/2, centerY-tempBitMap.getHeight()/2, circlePaint);
                    // canvas.drawCircle(centerX, centerY, radius, circlePaint);


                }
            }
            currentBody = currentBody.getNext();
        }

        //canvas.drawText("FPS:" + fPS + " Bodies: " + world.getBodyCount(), 20, 40, borderPaint);

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        Body body = bodyNearTouch(event);
        if (body != null) {
            ballWaitingToBeDeleted = body;
        }
        else {
            xForBallWaitingToBeCreated = event.getX();
            yForBallWaitingToBeCreated = event.getY();
            ballWaitingToBeCreated = true;
        }

        return super.onTouchEvent(event);
    }

    private Body bodyNearTouch(MotionEvent event) {
        Log.d("ElementH", "touch  " + event.getX() + ":" + event.getY());

        Body body = world.getBodyList();

        do {
            float x = BodyUtil.scaleToAndroidPixelCoord(body.getPosition().x);
            float y = BodyUtil.scaleToAndroidPixelCoord(body.getPosition().y);
            Shape shape = body.getShapeList();
            if (shape != null && ShapeType.CIRCLE_SHAPE.equals(shape.getType())) {
                CircleShape circleShape = (CircleShape) shape;
                int radius = BodyUtil.scaleToAndroidPixelCoord(circleShape.getRadius());

                if (event.getX() < x + radius && event.getX() > x - radius) {
                    if (event.getY() < y + radius && event.getY() > y - radius) {
                        return body;
                    }
                }
            }
            body = body.getNext();
        }
        while (body != null);
        return null;
    }

    public void updateWorld() {
        world.step(timeStep, iterations);
        if (ballWaitingToBeCreated) {
            ballWaitingToBeCreated = false;
            xForBallWaitingToBeCreated = BodyUtil.scaleFromAndroidPixelCoord(xForBallWaitingToBeCreated);
            yForBallWaitingToBeCreated = BodyUtil.scaleFromAndroidPixelCoord(yForBallWaitingToBeCreated);
            BodyUtil.createBall(world, xForBallWaitingToBeCreated, yForBallWaitingToBeCreated,BodyUtil.scaleFromAndroidPixelCoord(ballImage.getHeight()/2));
        }
        if (ballWaitingToBeDeleted!=null) {
            world.destroyBody(ballWaitingToBeDeleted);
            ballWaitingToBeDeleted = null;
        }
    }

}
